/*
 * Copyright [2025] [JinBooks of copyright http://www.jinbooks.com]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


package com.jinbooks.web.login.contorller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jinbooks.authn.LoginCredential;
import com.jinbooks.authn.LoginSecretKey;
import com.jinbooks.authn.dto.LoginConfigDto;
import com.jinbooks.authn.jwt.AuthJwt;
import com.jinbooks.authn.jwt.service.AuthTokenService;
import com.jinbooks.authn.provider.AbstractAuthenticationProvider;
import com.jinbooks.authn.secretkey.SecretKeyManager;
import com.jinbooks.configuration.ApplicationConfig;
import com.jinbooks.configuration.LoginConfig;
import com.jinbooks.constants.ConstsLoginType;
import com.jinbooks.entity.Institutions;
import com.jinbooks.entity.Message;
import com.jinbooks.entity.SocialsProvider;
import com.jinbooks.entity.SocialsProviderLogin;
import com.jinbooks.entity.config.ConfigLoginPolicy;
import com.jinbooks.persistence.service.LoginService;
import com.jinbooks.persistence.service.SocialsProviderService;
import com.jinbooks.sse.dto.SseMessageDto;
import com.jinbooks.sse.utils.SseMessageUtils;
import com.jinbooks.web.WebConstants;
import com.jinbooks.web.WebContext;
import com.jinbooks.web.login.service.IAuthStrategy;
import com.jinbooks.web.satoken.utils.LoginHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * 登录
 * <p>
 * 登录界面初始化/login/get
 * </p>
 *
 * <p>
 * 登录入口/login/signin
 * </p>
 *
 * @author Crystal.Sea
 *
 */
@RestController
@RequestMapping(value = "/login")
public class LoginEndpoint {
    private static final Logger logger = LoggerFactory.getLogger(LoginEndpoint.class);

    @Autowired
    AuthTokenService authTokenService;

    @Autowired
    ApplicationConfig applicationConfig;

    @Autowired
    LoginConfig loginConfig;

    @Autowired
    AbstractAuthenticationProvider authenticationProvider;

    @Autowired
    LoginService loginService;

    @Autowired
    SocialsProviderService socialsProviderService;

    @Autowired
    SecretKeyManager secretKeyManager;

    @Autowired
    ScheduledExecutorService scheduledExecutorService;

    /**
     * init login。登录界面初始化信息
     * @return
     */
    @GetMapping(value = {"/get"}, produces = {MediaType.APPLICATION_JSON_VALUE})
    public Message<LoginConfigDto> get() {
        logger.debug("/login.");
        LoginConfigDto conf = new LoginConfigDto();
        ConfigLoginPolicy loginPolicy = loginService.getConfigLoginPolicy();
        Institutions inst = (Institutions) WebContext.getAttribute(WebConstants.CURRENT_INST);
        conf.setInst(inst);
        conf.setCaptcha(loginPolicy.getCaptchaMgt().toUpperCase());
        conf.setState(authTokenService.genRandomJwt());
        LoginSecretKey loginSecretKey = secretKeyManager.getSecretKey();
        conf.setSecretKey(loginSecretKey.getSecretKey());
        conf.setSecretPublicKey(loginSecretKey.getPublicKey());

        // 设置社交登录提供商列表
        LambdaQueryWrapper<SocialsProvider> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SocialsProvider::getStatus, 1);
        if (inst != null) {
            //wrapper.eq(SocialsProvider::getBookId, inst.getId());
        }
        List<SocialsProvider> socialProviders = socialsProviderService.list(wrapper);
        conf.setSocials(new SocialsProviderLogin(socialProviders));
        return Message.ok(conf);
    }

    /**
     * 常规用户名和密码登录
     * @param credential
     * @return
     */
    @PostMapping(value = {"/signin"}, produces = {MediaType.APPLICATION_JSON_VALUE})
    public Message<AuthJwt> signin(@RequestBody LoginCredential credential) {
        Message<AuthJwt> authJwtMessage = Message.failed();
        secretKeyManager.decrypt(credential);

        // 授权类型
        String grantType = credential.getAuthType();
        if (ConstsLoginType.NORMAL.equals(credential.getAuthType()) && authTokenService.validateJwtToken(credential.getState())) {
            logger.debug("login in , credential {}", JSONUtil.toJsonPrettyStr(credential));
            Authentication authentication = authenticationProvider.authenticate(credential);
            if (authentication != null) {//success
                AuthJwt authJwt = IAuthStrategy.login(authentication, grantType);

                authJwtMessage.setData(Message.SUCCESS, authJwt);
            } else {//fail
                String errorMsg = WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE) == null ?
                        "" : WebContext.getAttribute(WebConstants.LOGIN_ERROR_SESSION_MESSAGE).toString();
                authJwtMessage.setMessage(errorMsg);
                logger.debug("login fail , message {}", errorMsg);
            }
        } else if (ConstsLoginType.SOCIALSIGNON.toLowerCase().contains(credential.getAuthType())) {
            AuthJwt authJwt = IAuthStrategy.login(credential, grantType);

            authJwtMessage.setData(Message.SUCCESS, authJwt);
        }

        Long userId = LoginHelper.getUserId();
        scheduledExecutorService.schedule(() -> {
            SseMessageDto dto = new SseMessageDto();
            dto.setMessage("欢迎登录后台管理系统");
            dto.setUserIds(List.of(userId));
            SseMessageUtils.publishMessage(dto);
        }, 5, TimeUnit.SECONDS);
        return authJwtMessage;
    }

}
